/**
 * This class is respnsible for dealing with all of memory. It initializes it when
 * the simulator starts. It also handles any changes made to memory. It deals with both
 * main memory and cache.
 */

package memsim;

import java.util.ArrayList;
import java.io.*;

//going to need to change the memorymanger class to accomodate for managing registers as well, if that is the route we want to take
public class MemoryManager
{

    //vars needed for the class
    ArrayList<Memory> ram = new ArrayList<Memory>();
    final int RAMSIZE = 100;
    final int WINDSIZE = 128;
    final int WIDTH = 2;
    String[] addressList;
    //cache goes here

    /*
     * Constructor which initializes all of memory. 
     */
    public MemoryManager()
    {
        addressList = new String[RAMSIZE];
        init();
        //init cache here
    }
    /**
     * @param  none
     * @return      list of the beginning of each section
     */
    public String[] getAddresses()
    {
        return addressList;
    }

    /**
     * This method initializes all of memory. It sets up all of the hexidecimal
     * addresses and sets all of the memory values to null.
     *
     * @param       none
     */
    public void init()
    {
        Object mem_allo[][];
        int address = 0;
        Memory mem;
        for(int i = 0; i < RAMSIZE; i++)
        {

            mem_allo = new Object[WINDSIZE][WIDTH];
            for(int j = 0; j < WINDSIZE; j++)
            {
                 //padding function -- ideal length, 8 - if not 8, add zeros
                //      finally, add 0x
                final int HEX_LEN = 8;
                String temp = Integer.toHexString(address);
                temp.toUpperCase();
                while(temp.length() < HEX_LEN)
                {
                    temp = "0" + temp;
                }
                temp = "0x" + temp;
                mem_allo[j][0] = temp;
                mem_allo[j][1] = 0;
                address += 4;
                if(j == 0)
                    addressList[i] = temp;

            }
            //create memory object with mem_allo
            mem =  new Memory(mem_allo, false);
            ram.add(mem);
        }
    }
    /**
     * This method resets all of memory.
     *
     * @param   none
     */
    public void clearMemory()
    {
        for(int i = 0; i < RAMSIZE; i++ )
        {
            Object[][] temp = ram.get(i).getData();
            for(int j = 0; j < WINDSIZE; j++)
            {
               temp[j][1] = 0;
            }
        }

    }

    /**
     * Method to retrieve the speed of the current memory
     *
     * @param  location  specify which page to check
     * @return           represets the amount of cyclees it takes ot access this type of memory.
     */
    public int getLatency(int location)
    {
        return ram.get(location).getSpeed();
    }

    /**
     * Method to retrieve an entire page of memory
     *
     * @param  location     specifcy which page to return
     * @return              copy of all the data in the page
     */
    public Object[][] getPage(int location)
    {
        return ram.get(location).getData();
    }

    /**
     * Method to set a page into memory replacing an old one.
     *
     * @param location  place the page will be insterted
     * @param           the page to be inserted
     */
    public void setPage(int location, Object[][] page)
    {
        Memory mem = new Memory(page, false);
        ram.set(location, mem);
    }

    /**
     * This method sets the chosen value in the correct page in a chosen slot.
     * 
     * @param addr      the address to be edited
     * @param value     the value to be inserted
     */
    public void setValue(String addr, Object value)
    {
        int pageNum = 0;
        int location = 0;

        String[] str = addr.toString().split("0x");
        
        int parsed = Integer.parseInt(str[1], 16);
        int temp = parsed;

        while((temp -= WINDSIZE) >= 0)
        {
            pageNum++;
        }

        

        location = (parsed - (WINDSIZE * pageNum)) / 4;

        pageNum /= 4;

        String str2 = value.toString();
        String[] tem = str2.split("0x");
        int insertValue = 0;

        if(tem.length == 1)
        {
            insertValue = Integer.parseInt(value.toString());
        }
        else
        {
            insertValue = Integer.parseInt(tem[1], 16);
        }

        
        ram.get(pageNum).setValue(location, insertValue);
    }

    /**
     * This method sets the chosen value in the correct page in a chosen slot.
     *
     * @param addr      the address to be edited
     * @param value     the value to be inserted
     */
    public void setValue(int addr, Object value)
    {
        int pageNum = 0;
        int location = 0;

        int temp = addr;

        while((temp -= WINDSIZE) >= 0)
        {
            pageNum++;
        }



        location = (addr - (WINDSIZE * pageNum)) / 4;

        pageNum /= 4;


        String str2 = value.toString();
        String[] tem = str2.split("0x");
        int insertValue = 0;

        if(tem.length == 1)
        {
            insertValue = Integer.parseInt(value.toString());
        }
        else
        {
            insertValue = Integer.parseInt(tem[1], 16);
        }


        ram.get(pageNum).setValue(location, insertValue);
    }

    /**
     * Method to get a value from a specifc slot in a page.
     *
     * @param addr      the slot in the requested page
     * @return          the value in the slot of the page
     */
    public Object getValue(String addr)
    {
        int pageNum = 0;
        int location = 0;

        String[] str = addr.toString().split("0x");

        System.out.println(str);
        int parsed = Integer.parseInt(str[1], 16);
        int temp = parsed;

        while((temp -= WINDSIZE) >= 0)
        {
            pageNum++;
        }

        location = (parsed - (WINDSIZE * pageNum)) / 4;

        pageNum /= 4;
        return ram.get(pageNum).getValue(location);
    }

    /**
     * Method to get a value from a specifc slot in a page.
     *
     * @param addr      the slot in the requested page
     * @return          the value in the slot of the page
     */
    public Object getValue(int addr)
    {
        int pageNum = 0;
        int location = 0;

        int temp = addr;

        while((temp -= WINDSIZE) >= 0)
        {
            pageNum++;
        }

        location = (addr - (WINDSIZE * pageNum)) / 4;

        pageNum /= 4;
        return ram.get(pageNum).getValue(location);
    }

    /**
     * This method writes the contents of memory to a file on disk.
     *
     * @param filename  name of the file to be written too
     */
    public void writeToDisk(String filename)
    {
        try
        {
            //use a comma seperated value format
            FileWriter writer = new FileWriter(filename);
            BufferedWriter output = new BufferedWriter(writer);
            Object [][] temp;
            for(int i = 0; i < RAMSIZE; i++)
            {
                temp = ram.get(i).getData();
                //System.out.println("AT: " + i + " @ " + temp[0][0]);
                
                for(int j = 0; j < WINDSIZE; j++)
                {
                    //if value is null write out a blank space
                    if(temp[j][1] == null)
                    {
                        output.write(temp[j][0] + "," + " " );
                        output.newLine();
                    }else
                    {
                        output.write(temp[j][0] + "," + temp[j][1]);
                        output.newLine();
                    }
                }
            }

            output.close();
        }
        catch (IOException e)
        {
            System.err.println("error: " + e.getMessage());
        }

    }

    /**
     * This method loads a specified file from disk into memory.
     *
     * @param filename  name of the file to be loaded
     */
    public void loadFromDisk(File filename)
    {
        try
        {
            FileInputStream fstream = new FileInputStream(filename);
            DataInputStream in = new DataInputStream(fstream);
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            String line = "";
            String[] temp = null;
            int memCounter = 0;
            int ramCounter = 0;
            //read in the file line by line keeping ocunter for where you
            //are in ram, and in the page
            while((line = reader.readLine()) != null)
            {
                
                temp = line.split(",");
                setValue(temp[0], temp[1]);
                
            }
            in.close();
        }
        catch (IOException e)
        {
            System.err.println("error: " + e.getMessage());
        }
    }
    public int height()
    {
        return WINDSIZE;
    }
    public int width()
    {
        return WIDTH;

    }
}